/**
 * 广寒宫
 * 网址:www.guanghangong.xyz
 */
package org.moon.framework.autoconfigure.knife4j;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;

import com.google.common.collect.Lists;
import org.moon.framework.autoconfigure.MoonConstants;
import org.moon.framework.autoconfigure.config.SystemConfig;
import org.moon.framework.autoconfigure.knife4j.properties.Knife4jProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.Profile;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;

import static java.util.Optional.ofNullable;

/**
 * knife4j配置
 * @author moon
 */
@EnableSwagger2WebMvc
@Configuration
@ConditionalOnClass(Docket.class)
@Profile({"local", "dev", "test"})
@EnableConfigurationProperties(Knife4jProperties.class)
public class Knife4jAutoConfiguration {

    @Autowired
    private SystemConfig systemConfig;

    private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/oauth/authorize", "/oauth/check_token","/oauth/token_key","/oauth/confirm_access","/oauth/error");
    private static final String SPLITOR = ";";

    @Bean(value = "defaultApi2")
    public Docket defaultApi2(Knife4jProperties knife4jProperties) {
        ParameterBuilder tokenPar = new ParameterBuilder();
        List<Parameter> pars = new ArrayList<Parameter>();
        tokenPar.name(MoonConstants.HEADER_TENANT_CODE).description("租户").defaultValue(MoonConstants.DEFAULT_TENANT_CODE).modelRef(new ModelRef("string")).parameterType("header").required(true).build();
        pars.add(tokenPar.build());
        tokenPar.name(MoonConstants.HEADER_ACCESS_TOKEN).description("令牌").defaultValue(knife4jProperties.getAccessToken()).modelRef(new ModelRef("string")).parameterType("header").required(false).build();
        pars.add(tokenPar.build());
        tokenPar.name(MoonConstants.REQUEST_FROM).description("请求来源").defaultValue(MoonConstants.RequestFormEnum.platform.name()).modelRef(new ModelRef("string")).parameterType("header").required(false).build();
        pars.add(tokenPar.build());

        ApiSelectorBuilder builder = new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(new ApiInfoBuilder()
                        .title(knife4jProperties.getTitle())
                        .description(knife4jProperties.getDescription())
                        .termsOfServiceUrl(knife4jProperties.getTermsOfServiceUrl())
                        .contact(new Contact(knife4jProperties.getContactName(),knife4jProperties.getContactUrl(),knife4jProperties.getContactEmail()))
                        .version(knife4jProperties.getVersion())
                        .license(knife4jProperties.getLicense())
                        .licenseUrl(knife4jProperties.getLicenseUrl())
                        .build())
                //分组名称
                .groupName(knife4jProperties.getGroupName()==null?systemConfig.getApplicationName():knife4jProperties.getGroupName())
                .globalOperationParameters(pars)
                .select()
                //这里指定Controller扫描包路径
                .apis(basePackage(knife4jProperties.getBasePackage()));
        //exclude-path处理
        if (knife4jProperties.getExcludePath()==null) {
            knife4jProperties.setExcludePath(Lists.newArrayList());
        }
        knife4jProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);
        knife4jProperties.getExcludePath().forEach(p -> builder.paths(PathSelectors.ant(p).negate()));
        return builder.build();
    }

    public static Predicate<RequestHandler> basePackage(final String basePackage) {
        return input -> declaringClass(input).map(handlerPackage(basePackage)::apply).orElse(true);
    }

    private static Function<Class<?>, Boolean> handlerPackage(final String basePackage)     {
        return input -> {
            for (String strPackage : basePackage.split(SPLITOR)) {
                boolean isMatch = input.getPackage().getName().startsWith(strPackage);
                if (isMatch) {
                    return true;
                }
            }
            return false;
        };
    }

    private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
        return ofNullable(input.declaringClass());
    }
}